﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Globalization;
using Ict.DataAccess.Conditions;

namespace Ict.DataAccess
{
    public abstract class DmlBuilder
    {
        /// <summary>
        /// 构造查询语句
        /// </summary>
        /// <param name="fieldList">查询字段列表，为null时使用*</param>
        /// <param name="tableName">查询表或视图名称</param>
        /// <param name="condition">查询条件</param>
        /// <param name="sortings">排序方式</param>
        /// <returns>查询语句</returns>
        public abstract string BuildQuery(string[] fieldList, string tableName, Condition condition, params Sort[] sortings);

        /// <summary>
        /// 构造查询语句
        /// </summary>
        /// <param name="fieldList">查询字段列表，为null时使用*</param>
        /// <param name="data">查询条件</param>
        /// <param name="tableName">查询表或视图名称</param>
        /// <param name="sortings">排序方式</param>
        /// <returns>查询语句</returns>
        public virtual string BuildQuery(string[] fieldList, DataEntity data, string tableName, params Sort[] sortings)
        {
            Dictionary<string, object> values = data.AssignValues;
            LogicCond condition = new LogicCond();
            if (values.Count > 0)
            {
                IEnumerator ikey = values.Keys.GetEnumerator();
                while (ikey.MoveNext())
                {
                    condition.Add(new RelationCond((string)(ikey.Current), data[(string)(ikey.Current)]));
                }
            }
            return BuildQuery(fieldList, tableName, condition, sortings);
        }

        /// <summary>
        /// 构造插入语句
        /// </summary>
        /// <param name="data">待插入的数据</param>
        /// <param name="tableName">表名</param>
        /// <returns>插入语句</returns>
        public abstract string BuildInsert(DataEntity data, string tableName);

        /// <summary>
        /// 构造更新语句
        /// </summary>
        /// <param name="data">待更新的数据</param>
        /// <param name="tableName">表名</param>
        /// <param name="condition">更新条件</param>
        /// <returns>更新语句</returns>
        public abstract string BuildUpdate(DataEntity data, string tableName, Condition condition);

        /// <summary>
        /// 构造删除语句
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="condition">删除条件</param>
        /// <returns>删除语句</returns>
        public abstract string BuildDelete(string tableName, Condition condition);

        /// <summary>
        /// 将对象指转成SQL的字符串
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected virtual string EncodeValue(object value)
        {
            if (value == null)
                return "NULL";
            Type t = value.GetType();
            string valueStr = "";
            if (t == typeof(string))
                valueStr = EncodeString(value as string, true);
            else if (t == typeof(DateTime))
                valueStr = EncodeDateTime((DateTime)value);
            else if (t == typeof(bool))
            {
                valueStr = (bool)value ? "1" : "0";
            }
            else if (t == typeof(DBNull))
                valueStr = "NULL";
            else if (t == typeof(Guid))
                valueStr = "'" + ((Guid)value).ToString() + "'";
            else
                valueStr = value.ToString();
            if (valueStr == "''")
                return "NULL";
            return valueStr;
        }

        protected virtual string EncodeString(string fieldValue, bool quote)
        {
            fieldValue = fieldValue.Replace("'", "''");
            if (quote)
                fieldValue = "'" + fieldValue + "'";
            return fieldValue;
        }

        protected virtual string EncodeDateTime(DateTime dateTime)
        {
            return "'" + dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + "'";
        }

        protected virtual string BuildSorting(Sort[] sortings)
        {
            IEnumerator i = sortings.GetEnumerator();
            i.MoveNext();
            string sql = " ORDER BY " + (i.Current as Sort).ToString();
            while (i.MoveNext())
            {
                sql += ", " + (i.Current as Sort).ToString();
            }
            return sql;
        }

        // 生成查询条件的方法
        #region BuildCondition

        /// <summary>
        /// 将查询条件类转换成字符串形式
        /// </summary>
        /// <param name="condition">查询条件类</param>
        /// <returns>条件的字符串形式</returns>
        protected virtual string BuildCondition(Condition condition)
        {
            if (condition is RelationCond)
            {
                return BuildRelationCondition(condition as RelationCond);
            }
            else if (condition is LogicCond)
            {
                return BuildLogicCondition(condition as LogicCond);
            }
            else if (condition is PlainCond)
            {
                return (condition as PlainCond).ToString();
            }
            else if (condition is LikeCond)
            {
                return BuildLikeCondition(condition as LikeCond);
            }
            else if (condition is InCond)
            {
                return BuildInCondition(condition as InCond);
            }
            else
            {
                throw new Exception("Unhandled DmlCondition Type.");
            }
        }

        protected virtual string BuildLikeCondition(LikeCond condition)
        {
            return (condition as LikeCond).FieldName + " LIKE " + EncodeValue((condition as LikeCond).Text);
        }

        protected virtual string BuildInCondition(InCond condition)
        {
            IEnumerator i = condition.Values.GetEnumerator() as IEnumerator;

            string sql = condition.FieldName + " IN (";
            sql += BuildInConditionValueList(i);
            sql += ")";
            return sql;
        }

        protected virtual string BuildInConditionValueList(IEnumerator i)
        {
            string sql = "";
            bool first = true;

            while (i.MoveNext())
            {
                if (!first)
                {
                    sql += ",";
                }
                if ((i.Current is IEnumerable) && !(i.Current is string))
                {
                    sql += BuildInConditionValueList((i.Current as IEnumerable).GetEnumerator());
                }
                else
                {
                    sql += EncodeValue(i.Current);
                }
                first = false;
            }

            return sql;
        }

        protected virtual string BuildLogicCondition(LogicCond condition)
        {
            if (condition.Operator == LogicCond.LogicOperator.Not)
            {
                return " NOT " + BuildCondition(condition.Conditions[0]);
            }
            else
            {
                string sql = null;
                string op = (condition.Operator == LogicCond.LogicOperator.Or) ? " OR " : " AND ";
                string sCond = null;
                IEnumerator<Condition> i = condition.Conditions.GetEnumerator() as IEnumerator<Condition>;
                if (i.MoveNext())
                {
                    sCond = BuildCondition(i.Current);
                    if (sCond != null && sCond != "")
                        sql = "(" + sCond + ")";
                    while (i.MoveNext())
                    {
                        sCond = BuildCondition(i.Current);
                        if (sCond != null && sCond != "")
                        {
                            if (sql != null)
                                sql += op;
                            sql += "(" + sCond + ")";
                        }
                    }
                }
                return sql;
            }
        }

        protected virtual string BuildRelationCondition(RelationCond condition)
        {
            string vstr = EncodeValue(condition.FieldValue);
            switch (condition.Operator)
            {
                case RelationCond.RelationOperator.Equal:
                    if (vstr == "NULL")
                        return condition.FieldName + " IS NULL";
                    return condition.FieldName + " = " + vstr;
                case RelationCond.RelationOperator.Notequal:
                    if (vstr == "NULL")
                        return condition.FieldName + " IS NOT NULL";
                    return condition.FieldName + " <> " + vstr;
                case RelationCond.RelationOperator.Less:
                    return condition.FieldName + " < " + vstr;
                case RelationCond.RelationOperator.Greater:
                    return condition.FieldName + " > " + vstr;
                case RelationCond.RelationOperator.LessEqual:
                    return condition.FieldName + " <= " + vstr;
                case RelationCond.RelationOperator.GreaterEqual:
                    return condition.FieldName + " >= " + vstr;
                default:
                    throw new Exception("Unknown operator type.");
            }
        }

        #endregion BuildCondition
    }
}
